/*                          UX-Framework                               
 *                                                                 
 *  iZE Co.,Ltd. Copyright 2009 All rights reserved.          
 *  This file is distributed under the terms of the GNU Lesser 
 *  General Public License, described in file LICENSE.                                     
 */

package jp.ize.ux.acts;

// {{{ imports
import jp.ize.ux.Util;
import jp.ize.ux.UXObject;
import jp.ize.ux.UXPoint;
import jp.ize.ux.acts.Act;

import feffects.Tween;
// }}}
// {{{ enum RingActEvent {
/**
 * このActが持つイベントの種類です。
 */
enum RingActEvent {
	START;     /* 動作が始まった */
	COMPLETE;  /* 動作が終わった */
}
// }}}
// {{{ typedef Rim = { >
/**
 * RingActが必要な関数、プロパティの定義です。
 */
typedef Rim = { >
	ListStateType,
	/** 中心座標 */
	var center(default, default) : UXPoint;
	/** 半径 */
	var radius(default, default) : Float;
	/** 回転具合 */
	var theta(default, default) : Float;
}
// }}}
// {{{ class RingRotateParameter extends ActParameter
/**
 * 回転パラメータを表現するクラス。
 */
class RingRotateParameter extends ActParameter
{
	// {{{ public var angle(default, default) : Int
	/**
	 * 回転量を角度で表現した数字です。
	 * 正の値は時計回り、負の値は反時計回りを意味します。
	 */
	public var angle(default, default) : Int;
	// }}}
}
// }}}
// {{{ class RingAct extends ActSubject<RingActEvent>
/**
 * RingAct クラス<br>
 * 円上に配置された動きを表現するクラス。中心座標を持つ状態を仮定します。<br>
 *
 * @author		iZE Co., Ltd.<br>
 * @version		$Id$<br>
 */
class RingAct extends ActSubject<RingActEvent>
{
	// {{{ public function new()
	/**
	 * コンストラクタ。
	 */
	public function new()
	{
		super();
	}
	// }}}
	// {{{ public function rotate( rotate_interp : Rim -> RingRotateParameter ) : Rim -> Void
	/**
	 * 子コンポーネントを回転させるアクションです。
	 */
	public function rotate( time : Float, omega : Float, ?rotate_interp : Rim -> RingRotateParameter ) : Rim -> Void
	{
		var me = this;
		return function( state : Rim ) {
			// me.state.touch();
			// . apply listener( START ).
			// . make some tweener.
			// . apply tweener over children.
			// . on tweener finish,
			//     . me.state.detouch();
			//     . notify listener( COMPLETE ).

			// just example.
			var goal = state.theta + omega;
			var time : Int = Math.round(time*1000);
			var tw = TweenUtil.createTween(state,
										   function(v) {state.theta = v; _layout(state); },
										   function (state) return state.theta,
										   goal, time, feffects.easing.Expo.easeOut);
			TweenUtil.startTween(tw);
		};
	}
	// }}}
	// {{{ public function layout( time : Int, ?interp : Rim -> RingRotateParameter ) : Rim -> Void
	/**
	 * 子コンポーネントをリング状に配置するアクションです。
	 */
	public function layout( time : Float, ?interp : Rim -> RingRotateParameter ) : Rim -> Void
	{
		var me = this;
		return function( state : Rim ) {
			for (i in 0...state.children.length) {
				var c = state.children[i];
				var time : Int = Math.round(time*1000);
				var goal_x = state.center.x + state.radius * Math.cos(state.theta + i*2*Math.PI / state.children.length);
				var goal_y = state.center.y - state.radius * Math.sin(state.theta + i*2*Math.PI / state.children.length);
				var tw_x = TweenUtil.createTween(c,
												 function(v) {c.x = v; },
												 function(c) return c.x,
												 goal_x, time,
												 feffects.easing.Expo.easeOut);
				var tw_y = TweenUtil.createTween(c,
												 function(v) {c.y = v; },
												 function(c) return c.y,
												 goal_y, time,
												 feffects.easing.Expo.easeOut);
				
				tw_x.start();
				tw_y.start();
			}
		};
	}
	// }}}
	#if flash10
	// {{{ public function rotateX( time : Float, angle : Int, ?interp : Rim -> RingRotateParameter ) : Rim -> Void
	public function rotateX( time : Float, angle : Int, ?interp : Rim -> RingRotateParameter ) : Rim -> Void
	{
		var me = this;
		return function( state : Rim ) {
			var time : Int = Math.round(time * 1000);
			var p : UXObject = cast (state.children[0].parent);
			var goal = p.rotationX + angle;
			var tw = TweenUtil.createTween(p, 
										   function(v) { p.rotationX = v; },
										   function(p) return p.rotationX,
										   goal, time,
										   feffects.easing.Expo.easeOut);
			TweenUtil.startTween(tw);
		}
	}
	// }}}
	#end
	// {{{ private static function _layout( state : Rim ) : Void
	/**
	 * 子コンポーネントを子コンポーネントのリサイズポリシーに従って円上に配置します。
	 */
	private static function _layout( state : Rim ) : Void
	{
		for (i in 0...state.children.length) {
			var c = state.children[i];
			c.x = state.center.x + state.radius * Math.cos(state.theta + i*2*Math.PI / state.children.length);
			c.y = state.center.y - state.radius * Math.sin(state.theta + i*2*Math.PI / state.children.length);
		}
		retouchZ(state);
	}
	// }}}
	// {{{ private static function retouchZ( state : Rim ) : Void

	private static function retouchZ( state : Rim ) : Void
	{
		#if flash10
		//重なり具合を調節する。
		// z_of : UXObject -> Float グローバルなz座標を求める関数
		var z_of : UXObject -> Float = function (c) {
 			var v = new flash.geom.Vector3D(c.x, c.y, c.z);
			var p : flash.display.Sprite = Reflect.field(c.parent, "sprite");
			if (p.transform.matrix3D != null) {
				return p.transform.matrix3D.transformVector(v).z;
			}
			return 0;
		}
		var comp : UXObject -> UXObject -> Int = function(c1, c2)
			return Math.round(z_of(c2) - z_of(c1));
		var cs = state.children.copy();
		cs.sort(comp);
		for (i in 0...cs.length) {
			var child = cs[i];
			child.parent.setChildIndex(child, i);
		}
		#end
	}
	
	// }}}
}
// }}}
